<?php
class remote_file {
	
	public function get_file ($url, $get_header=false) {
		if ($url) {
			if (preg_match('/^http:\/\/|^https:\/\//i', $url)) {
				$ch = curl_init();
				if (preg_match('/^https:\/\//i', $url)) {					// 如果是https开头的，那么关闭ssl证书验证
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
				}
				curl_setopt ($ch, CURLOPT_URL, $url);
				curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1); 
				curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, 10);
				curl_setopt ($ch, CURLOPT_ENCODING, '');					// The contents of the "Accept-Encoding: " header. This enables decoding of the response. Supported encodings are "identity", "deflate", and "gzip". If an empty string, "", is set, a header containing all supported encoding types is sent. 
				curl_setopt ($ch, CURLOPT_FOLLOWLOCATION, true);			// TRUE to follow any "Location: " header that the server sends as part of the HTTP header (note this is recursive, PHP will follow as many "Location: " headers that it is sent, unless CURLOPT_MAXREDIRS is set). 
				curl_setopt ($ch, CURLOPT_MAXREDIRS, 10);
				if ($get_header) {
					curl_setopt ($ch, CURLOPT_HEADER, true);
				}

				$return = curl_exec($ch);
				curl_close($ch);
				return ($return) ? $return : false;
			} else return false;
			
			// -------------------------
			
//			$opts = array (
//				'http' => array (
//					'method'	=> "GET",
//					'timeout'	=> 10,
//				)
//			);
//			$context = stream_context_create($opts);
//			$return = file_get_contents($url, false, $context);
//			return ($return) ? $return : false;
			
			/**
			 * 当需要获得HTTP header信息的时候，可以考虑用sock的方式
			 * 
			 * 返回如下
			 * HTTP/1.1 301 Moved Permanently
			 * Server: nginx/0.9.2
			 * Date: Fri, 31 Dec 2010 09:24:37 GMT
			 * Content-Type: text/html
			 * Content-Length: 184
			 * Connection: close
			 * Location: http://www.sina.com/
			 * 
			 * 
			 * HTTP/1.0 200 OK
			 * Content-Type: text/html
			 * Last-Modified: Fri, 07 Jan 2011 06:47:37 GMT
			 * Accept-Ranges: bytes
			 * ETag: "628254c536aecb1:0"
			 * Server: Microsoft-IIS/7.5
			 * X-Powered-By: ASP.NET
			 * Date: Wed, 12 Jan 2011 02:38:52 GMT
			 * Content-Length: 3106
			 * Vary: Accept-Encoding
			 * Content-Encoding: gzip
			 * Age: 23653
			 * Node: HIT from CNTC-ZJ-42.246
			 * Via: 1.0 CNTC-ZJ-42.246:80 (CUWEBCACHE/1.4.0.1)
			 * Connection: close
			 */
			
			/* 
			$fp = fsockopen("sina.com", 80, $errno, $errstr, 3);
			if (!$fp) {
			    echo "$errstr ($errno)<br />\n";
			} else {
			    $out = "GET / HTTP/1.1\r\n";
			    $out .= "Host: sina.com\r\n";
			    $out .= "Connection: Close\r\n\r\n";
			
			    fwrite($fp, $out);
			    while (!feof($fp)) {
			        echo fgets($fp, 128);
			    }
			    fclose($fp);
			}
			*/
			
		} else return false;
	}
	
	/* HTML 部分 ------------------------------------------------------ */
	
	/**
	 * 根据URL解析出html代码中的rss链接
	 * 		解析file中的alternate的href, 寻找类型：<link rel="alternate" type="application/rss+xml" href="http://blog.sina.com.cn/rss/1232203944.xml" title="RSS" />
	 *
	 * @param String $url
	 * @return String
	 */
	public function parse_rss_href ($url) {
		if ($url) {
			$file = $this->get_file($url);
			if ($file) {
				$return = false;
				preg_match_all("/<link\s+.+?>/is", $file, $match);
				foreach ($match[0] as $item) {
					if (preg_match('/rel=(\'|\")alternate(\'|\")/i', $item) and preg_match('/type=(\'|\")application\/rss\+xml(\'|\")/i', $item)) {
						preg_match('/href=(\'|\")(.+?)(\'|\")/i', $item, $href);
						$return = $href[2];
						break;
					}
				}
				return $return;
			} else return false;
		} else return false;
	}
	/**
	 * 根据URL返回页面内容
	 *
	 * @param String $url
	 * @return Array = array (
	 * 				目前直接返回body
	 * 
	 * 
	 * 
	 * 		file		// 原始html，包括http header，没有转换字符集
	 * 		charset		// 各种方式检测的字符集
	 * 		body		// 转成utf-8的body中的内容，去掉了js和html注释
	 * )
	 */
	public function parse_content ($url) {
		if ($url) {
			$file = $this->get_file($url, 'get_header');
			if ($file) {
				// 把header部分分离出来
				$split = explode("\r\n\r\n", $file, 2);
				
				// 用各种方法获取charset
				$charset = $this->charset_from_header($split[0]);									// 从header中获取charset
				if (empty($charset)) {
					$charset = $this->charset_from_html($split[1]);									// 从html中获取的charset
					if ($charset and ($charset=='GB2312' or $charset=='GBK' or $charset=='GB18030')) {
						if ($this->utf_or_gb($split[1]) === 'UTF-8') {								// 中文页面中有些meta中的编码标记是错误的，所以要再检查一遍，对于非中文页面，只好他标什么就是什么了
							$charset = 'UTF-8';
						}
					}
				}
				
				// 获取body中的部分
				$split = preg_split('/<body.*?>/is', $split[1]);
				$split = preg_split('/<\/body/is', $split[1]);
				$body = preg_replace('/<script.*?<\/script>|<!--.*?-->/is', '', $split[0]);		// 去掉body中的javascript和注释
				
				// 如果有<header><footer>之类的标签，统统去掉
				$body = preg_replace('/<header.*?>.*?<\/header.*?>/is', '', $body);
				$body = preg_replace('/<footer.*?>.*?<\/footer.*?>/is', '', $body);
				$body = preg_replace('/<style.*?>.*?<\/style.*?>/is', '', $body);
				
				// 去掉iframe
				$body = preg_replace('/<iframe.*?>.*?<\/iframe.*?>/is', '', $body);
				
				
				// 转换字符集为utf-8
				if ($charset and $charset<>'UTF-8') {
					if ($charset == 'GB2312' or $charset == 'GBK' or $charset == 'GB18030' or $charset == 'CP936' or $charset == 'EUC-CN' or $charset == 'HZ') {
						$body = iconv('GB18030', 'UTF-8', $body);
					} else {
						$body = iconv($charset, 'UTF-8', $body);
					}
				}
				
//				$return['file'] 		= $file;
//				$return['charset'] 		= $charset;
//				$return['body'] 		= $body;
				
				return $body;
			} else return false;
		} else return false;
	}
	private function utf_or_gb ($string) {
		$return = 'UTF-8';
	    for ($i=0; $i<strlen($string); $i++) {  
	        if(ord($string{$i})<128)  
	            continue;  
	        if((ord($string{$i})&224)==224) {  				//第一个字节判断通过
	            $char = $string{++$i};  
	            if((ord($char)&128)==128) {  				//第二个字节判断通过
	                $char = $string{++$i};  
	                if((ord($char)&128)==128) {
	                	$return = 'UTF-8';
	                    break;  
	                }  
	            }  
	        }  
	        if((ord($string{$i})&192)==192) {  				//第一个字节判断通过
	            $char = $string{++$i};  
	            if((ord($char)&128)==128) {  				//第二个字节判断通过
	                $return = 'GB2312';
	                break;  
	            }  
	        }  
	    }
	    return $return;
	}
	private function is_uft8_2 ($string) {
		// 这种方式效率不如is_utf8
		if (preg_match("/^([".chr(228)."-".chr(233)."]{1}[".chr(128)."-".chr(191)."]{1}[".chr(128)."-".chr(191)."]{1}){1}/",$string) == true || preg_match("/([".chr(228)."-".chr(233)."]{1}[".chr(128)."-".chr(191)."]{1}[".chr(128)."-".chr(191)."]{1}){1}$/",$string) == true || preg_match("/([".chr(228)."-".chr(233)."]{1}[".chr(128)."-".chr(191)."]{1}[".chr(128)."-".chr(191)."]{1}){2,}/",$string) == true)	{
			return true;
		} else {
			return false;
		}
	}
	private function charset_from_header ($header) {
		if ($header) {
			preg_match('/^Content-Type:.*?charset=([\w|-]+)/im', $header, $match);
			if ($match[1]) {
				return $match[1];
			} else return false;
		} else return false;
	}
	private function charset_from_html ($html) {
		if ($html) {
			preg_match_all('/<meta.*?>/is', $html, $meta);
			if (is_array($meta[0])) {
				foreach ($meta[0] as $item) {
					preg_match('/charset\W+([\w|-]+)\W+/is', $item, $match);
					if ($match[1]) {
						return strtoupper($match[1]);
					}
				}
				return false;
			} else return false;
		} else return false;
	}

	
	/* XML 部分 ------------------------------------------------------ */
	
	/**
	 * 
	 * 解析rss的xml文件
	 *
	 * @param String $url
	 * @return Array  = array (
	 * 		'url'			=>				// rss 文件的url
	 * 		'title'			=>
	 * 		'link'			=>				// xml中的link，即博客的首页
	 * 		'description'	=>
	 * 		'items'			=> array (
	 * 			array (
	 * 				'title'
	 * 				'link'
	 * 				'pubdate'
	 * 				'description'
	 * 				'content'
	 * 			)
	 * 		)
	 * )
	 */
	public function parse_xml ($url) {
		$xml = $this->get_file($url);					// 获取远程文件内容，失败返回false
		if ($xml) {
			$xml = $this->anything_to_utf8($xml, '', 'xml');
			$return = $this->parse_xml_my($xml);
			$return['url']	= $url;							// 把url也作为返回内容
			// 特殊情况，如果link没有解析出来
			if (empty($return['link'])) {
				$p = parse_url($url);
				$return['link'] = $p['scheme'].'://'.$p['host'];		// 把顶级url作为link
			}
			return $return;
		} else return false;
	}
	

	

	
	public function anything_to_utf8 ($str, $charset='', $file_type='') {
		if (empty($charset)) {									// 如果没有指定字符集，则尝试分析字符集
			if ($file_type == 'xml') {							// xml文件类型，直接查找源码中标记编码
				$charset = $this->get_xml_encoding($str);
			} elseif ($file_type == 'html') {					// html文件类型，直接查找源码中标记编码
				$charset = $this->charset_from_html($str);
			} else {											// 未指定类型，尝试html和xml
				$charset = $this->charset_from_html($str);			// 首先尝试html
				if (empty($charset)) {
					$charset = $this->get_xml_encoding($str);		// 然后再尝试xml
				}
			}
			
			if (empty($charset)) {	// 如果仍然不能确定charset
				$charset = mb_detect_encoding($str, array("UTF-8", "GB2312", "GBK", "BIG-5"));			// 这样的结果很不准确，返回结果：UTF-8，CP936， EUC-CN， (BIG-5好像检测不出来)
			}
		}
		
		// 下面开始转换编码
		if ($charset) {
			$charset = strtoupper($charset);
			if ($charset == 'GB2312' or $charset == 'GBK' or $charset == 'GB18030' or $charset == 'CP936' or $charset == 'EUC-CN' or $charset == 'HZ') {
				$str = iconv('GB18030', 'UTF-8', $str);
			} else {
				$str = iconv($charset, 'UTF-8', $str);
			}
			return $str;
		} else return $str;				// 如果无法确认编码，则返回原字符串
	}
	private function get_xml_encoding ($xml) {
		/** 如果是xml文件
		 * <?xml version="1.0" encoding="utf-8" ?>
		 */ 
		preg_match('/<\?xml.*?encoding=["|\'](.*?)["|\'].*?\?>/is', $xml, $match);
		if ($match[1]) {
			return $match[1];
		} else return false;
	}
	
	/**
	 * 解析xml文档
	 * 
	 * 
	 *
	 * @param String $content
	 * @return array = array (
	 * 		title
	 * 		link
	 * 		description
	 * 		items	= array (
	 * 			array (
	 * 				title
	 * 				link
	 * 				pubdate
	 * 				description
	 * 				content
	 * 			)
	 * 			...
	 * 		)
	 * )
	 */
	private function parse_xml_my ($content) {
		if ($content) {
			preg_match('/<channel.*?>.*?<item.*?>/is', $content, $match);		// [0]
			$return = $this->parse_xml_header($match[0]);
			
			preg_match_all('/<item.*?>.*?<\/item.*?>/is', $content, $match);
			if (is_array($match[0])) {
				foreach ($match[0] as $v) {
					$return['items'][] = $this->parse_xml_item($v);
				}
			}
			return $return;
		} else return false;
	}
	private function parse_xml_header ($str) {
		preg_match('/<title.*?>(.*?)<\/title.*?>/is', $str, $match);
		$return['title'] = $this->trim_cdata($match[1]);
		
		preg_match('/<link.*?>(.*?)<\/link.*?>/is', $str, $match);
		$return['link'] = $this->trim_cdata($match[1]);
		
		preg_match('/<description.*?>(.*?)<\/description.*?>/is', $str, $match);
		$return['description'] = $this->trim_cdata($match[1]);
		
		return $return;
	}
	private function parse_xml_item ($str) {
		preg_match('/<title.*?>(.*?)<\/title.*?>/is', $str, $match);
		$return['title'] = $this->trim_cdata($match[1]);
		
		preg_match('/<link.*?>(.*?)<\/link.*?>/is', $str, $match);
		$return['link'] = $this->trim_cdata($match[1]);

		preg_match('/<pubDate.*?>(.*?)<\/pubDate.*?>/is', $str, $match);
		$return['pubdate'] = $this->trim_cdata($match[1]);

		preg_match('/<description.*?>(.*?)<\/description.*?>/is', $str, $match);
		$return['description'] = $this->trim_cdata($match[1]);
		
		preg_match('/(<content.*?>)(.*?)<\/content.*?>/is', $str, $match);
		if (preg_match('/encoded/i', $match[1])) {							// 如果标签为<content:encoded>，则html解码
			$return['content'] = html_entity_decode($this->trim_cdata($match[2]));
		} else {
			$return['content'] = $this->trim_cdata($match[2]);
		}
		
		return $return;
	}
	private function trim_cdata ($str) {
		$str = trim($str);
		preg_match('/^<!\[CDATA\[(.*?)\]\]>$/is', $str, $match);
		if ($match[1]) {
			return trim($match[1]);
		} else return $str;
	}
	
function safeEncoding($string,$outEncoding = 'UTF-8') {  

    
    return $encoding;
      
    if(strtoupper($encoding) == strtoupper($outEncoding))  
        return $string;  
    else 
        return iconv($encoding,$outEncoding,$string);  
} 

function get_body ($file) {
	$split = preg_split('/<body.*?>/is', $file);
	$split = preg_split('/<\/body/is', $split[1]);
	
				$file = preg_replace('/<script.*?<\/script>/is', '', $split[0]);
				$file = preg_replace('/<!--.*?-->/is', '', $file);

	return $file;
}
	
// -- 以下好像用不到了 ----------------------------------------------------------
	private function parse_xml_DOM ($xml) {
		$return['url']	= $url;							// 把url也作为返回内容
		
		$xml = $this->xml_to_utf8($xml);				// 整理xml内容，统一转换成utf8编码
		if ($xml) {
			// 初始化 domDocument 对象
			$dom = new DOMDocument();
			$dom->loadXML($xml);
			
			// 处理 channel 的基本信息
			$channel = $dom->getElementsByTagName('channel')->item(0);
			
			foreach ($channel->childNodes as $c) {
				if ($c->nodeType == 1) {							// 必须是ELEMENT_NODE=1 (foreach出来的还有TEXT_NODE=3)
					switch (strtolower($c->nodeName)) {
						case 'title':
							$return['title'] = $c->nodeValue;
							break;
						case 'link':
							$return['link'] = $c->nodeValue;
							break;
						case 'description':
							$return['description'] = $c->nodeValue;
							break;
						default:
							// nothing
							break;
					}
				}
			}
			// 处理 item 信息
			$items = $dom->getElementsByTagName('item');
			if ($items->length > 0) {
				foreach ($items as $item) {
					$tmp = '';
					foreach ($item->childNodes as $c) {
						if ($c->nodeType == 1) {							// 必须是ELEMENT_NODE=1 (foreach出来的还有TEXT_NODE=3)
							switch (strtolower($c->nodeName)) {
								case 'title':
									$tmp['title'] = $c->nodeValue;
									break;
								case 'link':
									$tmp['link'] = $c->nodeValue;
									break;
								case 'pubdate':
									$tmp['pubdate'] = $c->nodeValue;
									break;
								case 'description':
									$tmp['description'] = $c->nodeValue;
									break;
								case 'content':								// 好像是atom独有，rss没有， 标签是<content:encoded>，还有<content:encoded lang="gbk">（差不多，记不清了，好像影响不大），这个里面会带html格式，subscribe里不带html（但是也有带的）
									$tmp['content'] = $c->nodeValue;
									break;
								default:
									// nothing
									break;
							}
						}
					}
					$return['items'][] = $tmp;
				}
			}
			// 去掉title和description的首尾空格
			$return['title'] 		= trim($return['title']);
			$return['description'] 	= trim($return['description']);
			
			// 检查结果并返回数组，或者返回错误代码
			if ($return['title'] and $return['link'] and count($return['items'])>0) {		// title，link和items都存在时，返回结果
				return $return;
			} else {									// 否则标记错误信息，返回错误代码
				if (!$return['title']) {					// -1 : 没有解析到title
					return -1;
				} elseif (!$return['link']) {				// -2 : 没有解析到link
					return -2;
				} else {									// -3 : 没有解析到items或者其他
					return -3;
				}
			}
		} else return false;
	}
	private function xml_to_utf8 ($xml) {
		if ($xml) {
			// 去掉首尾空格
			$xml = trim($xml);
			// 从当前字符集转换为utf-8
			$xml = $this->anything_to_utf8($xml);
			// 改写xml中的标签为utf-8
			$xml = preg_replace('/encoding=".+"/i', 'encoding="utf-8"', $xml, 1);
			// 去掉xml中注释
			$xml = preg_replace('/<!--.+?-->/is', '', $xml);			// 这在处理http://feed.williamlong.info/的时候有问题，把所有的内容都给清除了！！！修改了一下，应该好了吧
			// 改写cdata中的内容，好像没必要了
			//$xml = preg_replace("/\<\!\[CDATA\[(.*?)\]\]\>/ies", "'[CDATA]'.base64_encode('$1').'[/CDATA]'", $xml);
			return $xml;
		} else {
			return false;
		}
	}
	
}
?>